package com.youhome.service.service.impl;

import com.youhome.service.model.Point;
import com.youhome.service.service.GeoService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by chen365 on 2019/02/20.
 */
@Service
public class GeoServiceImpl implements GeoService {

    /**
     * 返回一个点是否在一个多边形边界上
     *
     * @param mPoints 多边形坐标点列表
     * @param point   待判断点
     * @return true 点在多边形边上,false 点不在多边形边上。
     */
    public static boolean isPointInPolygonBoundary(List<Point> mPoints, Point point) {
        for (int i = 0; i < mPoints.size(); i++) {
            Point p1 = mPoints.get(i);
            Point p2 = mPoints.get((i + 1) % mPoints.size());
            // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数

            // point 在p1p2 底部 --> 无交点
            if (point.getLat() < Math.min(p1.getLat(), p2.getLat()))
                continue;
            // point 在p1p2 顶部 --> 无交点
            if (point.getLat() > Math.max(p1.getLat(), p2.getLat()))
                continue;

            // p1p2是水平线段,要么没有交点,要么有无限个交点
            if (p1.getLat() == p2.getLat()) {
                double minX = Math.min(p1.getLng(), p2.getLng());
                double maxX = Math.max(p1.getLng(), p2.getLng());
                // point在水平线段p1p2上,直接return true
                if ((point.getLat() == p1.getLat()) && (point.getLng() >= minX && point.getLng() <= maxX)) {
                    return true;
                }
            } else { // 求解交点
                double x = (point.getLat() - p1.getLat()) * (p2.getLng() - p1.getLng()) / (p2.getLat() - p1.getLat()) + p1.getLng();
                if (x == point.getLng()) // 当x=point.getLng()时,说明point在p1p2线段上
                    return true;
            }
        }
        return false;
    }


    /**
     * 返回一个点是否在一个多边形区域内
     *
     * @param points 多边形坐标点列表
     * @param point  待判断点
     * @return true 多边形包含这个点,false 多边形未包含这个点。
     */
    @Override
    public boolean isPolygonContainsPoint(List<Point> points, Point point) {
        int nCross = 0;
        for (int i = 0; i < points.size(); i++) {
            Point p1 = points.get(i);
            Point p2 = points.get((i + 1) % points.size());
            // 取多边形任意一个边,做点point的水平延长线,求解与当前边的交点个数
            // p1p2是水平线段,要么没有交点,要么有无限个交点
            if (p1.getLat() == p2.getLat())
                continue;
            // point 在p1p2 底部 --> 无交点
            if (point.getLat() < Math.min(p1.getLat(), p2.getLat()))
                continue;
            // point 在p1p2 顶部 --> 无交点
            if (point.getLat() >= Math.max(p1.getLat(), p2.getLat()))
                continue;
            // 求解 point点水平线与当前p1p2边的交点的 X 坐标
            double x = (point.getLat() - p1.getLat()) * (p2.getLng() - p1.getLng()) / (p2.getLat() - p1.getLat()) + p1.getLng();
            if (x > point.getLng()) // 当x=point.getLng()时,说明point在p1p2线段上
                nCross++; // 只统计单边交点
        }
        // 单边交点为偶数，点在多边形之外 ---
        return (nCross % 2 == 1);
    }

    @Override
    public boolean isUSAPolygonContainsPoint(Point point) {

        // 美国大致范围坐标点
        Point p1 = new Point(-130.3171287, 49.364086);//美国西北角
        Point p2 = new Point(-62.8134377, 50.194331);//美国东北角
        Point p3 = new Point(-79.7698077, 24.556773);//美国东南角
        Point p4 = new Point(-126.0612187, 27.478862);//美国西南角

        List<Point> ps = new ArrayList<>();
        ps.add(p1);
        ps.add(p2);
        ps.add(p3);
        ps.add(p4);
        ps.add(p1);

        return isPolygonContainsPoint(ps, point);
    }

    @Override
    public boolean isUSAPolygonContainsPoint(Double lng, Double lat) {
        Point point = new Point(lng, lat);
        return isUSAPolygonContainsPoint(point);
    }
}
